/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.dac;

import com.inspur.edp.caf.generator.BaseGenerator;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.property.ClassPropertyGenerator;
import com.inspur.edp.cef.api.attr.ChildAssemblerAttribute;
import com.inspur.edp.cef.api.attr.ExtendChildAssemblerAttribute;
import com.inspur.edp.cef.gencommon.ReflectionUtils;
import com.inspur.edp.cef.generator.repository.AssemblerInfo;
import com.inspur.edp.cef.generator.repository.adaptor.BaseAdaptorGenerator;
import com.inspur.edp.cef.generator.repository.adaptor.adaptoritem.RootNodeEntityAdaptorItemGenerator;
import com.inspur.edp.cef.generator.repository.dac.method.*;
import com.inspur.edp.cef.generator.repository.dac.property.NodeCodePropGenerator;
import com.inspur.edp.cef.generator.repository.repository.nestedrepository.DataTypeReposGenContext;
import com.inspur.edp.cef.repository.assembler.DataTypeAssembler;
import com.inspur.edp.das.commonmodel.IGspCommonObject;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Map;

public abstract class EntityDacGenerator extends DataTypeDacGenerator {
    protected String NodeCode;
//    protected AssemblerInfo BaseAssembler;
    protected java.util.ArrayList<AssemblerInfo> ExtendAssemblers = new java.util.ArrayList<AssemblerInfo>();
    protected java.util.HashMap<String, AssemblerInfo> ChildAssemblerTypes = new java.util.HashMap<String, AssemblerInfo>();
    protected java.util.HashMap<String, java.util.ArrayList<AssemblerInfo>> ChildExtendAssemblers = new java.util.HashMap<String, java.util.ArrayList<AssemblerInfo>>();
    protected IGspCommonObject gspCommonObject;

    protected abstract boolean getIsChildDac();


//ORIGINAL LINE: internal new EntityDacGenContext Context => (EntityDacGenContext)super.Context;
    public EntityDacGenContext getEntityDacGenContext() {
        return (EntityDacGenContext) super.getContext();
    }

    protected EntityDacGenerator(IGspCommonObject gspCommonObject, DataTypeReposGenContext reposContext, String nodeCode) {
        super(reposContext);
        this.gspCommonObject = gspCommonObject;
        NodeCode = nodeCode;
        //BaseAssembler还没赋值, initChildAssemblerInfo(BaseAssembler)里取不到
        //InitAssemblerInfo();
    }

    @Override
    protected void beforeInitialize() {
        super.beforeInitialize();
        initAssemblerInfo();
    }

    private void initAssemblerInfo() {
        initAssemblerType();
        try {
            //todo wangmj
            initChildAssemblerInfo(null);
            intiExtendAssemblerInfo();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException("实例化失败", e);
        }

    }

    protected abstract void initAssemblerType();

    //todo wangmj
    protected final void initChildAssemblerInfo(AssemblerInfo info) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        if (info == null) {
//            return;
//        }
//
//        Object[] childAttrs = ReflectionUtils.getAnnotations(info.getAssemblerType(), ChildAssemblerAttribute.class);
//
//        if (childAttrs.length == 0) {
//            return;
//        }
//
//        for (Object childAttrObj : childAttrs) {
//            ChildAssemblerAttribute childAttr = (ChildAssemblerAttribute) childAttrObj;
//
//            String nodeCode = ((ChildAssemblerAttribute) ((childAttr instanceof ChildAssemblerAttribute) ? childAttr : null)).nodeCode();
//
//            Class childType = ((ChildAssemblerAttribute) ((childAttr instanceof ChildAssemblerAttribute) ? childAttr : null)).assemblerType();
//            if (ChildAssemblerTypes.containsKey(nodeCode)) {
//                throw new RuntimeException("不允许添加重复的子表");
//            }
//            Object tempVar = childType.getConstructor(null).newInstance(null);
//
//            DataTypeAssembler childInstance = (DataTypeAssembler) ((tempVar instanceof DataTypeAssembler) ? tempVar : null);
//            AssemblerInfo tempVar2 = new AssemblerInfo();
//            tempVar2.setAssemblerType(childType);
//            tempVar2.setAssemblerInstance(childInstance);
//            ChildAssemblerTypes.put(nodeCode, tempVar2);
//        }
    }

    protected abstract void intiExtendAssemblerInfo() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException;

    protected final void initExtendChildAssemblerInfo(AssemblerInfo info) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Object[] childExtendAttrs = ReflectionUtils.getAnnotations(info.getAssemblerType(), ExtendChildAssemblerAttribute.class);

        if (childExtendAttrs.length == 0) {
            return;
        }

        for (Object childextendAttrObj : childExtendAttrs) {
            ExtendChildAssemblerAttribute childextendAttr = (ExtendChildAssemblerAttribute) childextendAttrObj;


            String nodeCode = ((ExtendChildAssemblerAttribute) ((childextendAttr instanceof ExtendChildAssemblerAttribute) ? childextendAttr : null)).nodeCode();

            Class childExtendType = ((ExtendChildAssemblerAttribute) ((childextendAttr instanceof ExtendChildAssemblerAttribute) ? childextendAttr : null)).assemblerType();
            //ExtendList应有顺序？按照层级关系？
            if (!ChildAssemblerTypes.containsKey(nodeCode)) {
                throw new RuntimeException("不存在编号为" + nodeCode + "的子表");
            }
            Object tempVar = ReflectionUtils.createInstance(childExtendType);

            DataTypeAssembler childInstance = (DataTypeAssembler) ((tempVar instanceof DataTypeAssembler) ? tempVar : null);
            if (!ChildExtendAssemblers.containsKey(nodeCode)) {
                ChildExtendAssemblers.put(nodeCode, new java.util.ArrayList<AssemblerInfo>());
            }
            AssemblerInfo tempVar2 = new AssemblerInfo();
            tempVar2.setAssemblerType(childExtendType);
            tempVar2.setAssemblerInstance(childInstance);
            ChildExtendAssemblers.get(nodeCode).add(tempVar2);
        }
    }

    @Override
    protected DataTypeDacGenContext createDacGenContext() {
        EntityDacGenContext context = new EntityDacGenContext();
        context.setNodeCode(NodeCode);
        context.setReposContext(ReposContext);
        return context;
    }

    @Override
    protected java.util.ArrayList<ClassPropertyGenerator> createPropertyGenerators() {
        return new java.util.ArrayList<ClassPropertyGenerator>(java.util.Arrays.asList(new ClassPropertyGenerator[]{new NodeCodePropGenerator(NodeCode)}));
    }

    @Override
    protected java.util.ArrayList<ClassMethodGenerator> createMethodGenerators() {

        ArrayList<ClassMethodGenerator> methods = super.createMethodGenerators();
        if (methods == null) {
            methods = new java.util.ArrayList<ClassMethodGenerator>();
        }
        if (getEntityDacGenContext().getChildDacTypes() != null
            && getEntityDacGenContext().getChildDacTypes().size() > 0)
            methods.add(new GetChildDacMethodGenerator(getEntityDacGenContext()));
        EntityDacGenContext entityDacGenContext = (EntityDacGenContext) getContext();
        methods
            .add(new CreateInstanceMethodGenerator(entityDacGenContext, NodeCode, getIsChildDac()));
        methods.add(new GetVersionMethodGenerator());
        return methods;
    }

    @Override
    protected java.util.ArrayList<BaseGenerator> createClassExtendChildGenrators() {

        ArrayList<BaseGenerator> extendGens = super.createClassExtendChildGenrators();
        if (extendGens == null) {
            extendGens = new java.util.ArrayList<BaseGenerator>();
        }
        addChildDacGenerator(extendGens);
        EntityDacGenContext entityDacGenContext = (EntityDacGenContext) getContext();
        //TODO DbTypeList;
        RootNodeEntityAdaptorItemGenerator entityItemGen = new RootNodeEntityAdaptorItemGenerator(gspCommonObject, entityDacGenContext, NodeCode, getIsChildDac());

        BaseAdaptorGenerator baseGen = new BaseAdaptorGenerator(gspCommonObject, entityDacGenContext, NodeCode, getIsChildDac());
        getEntityDacGenContext().getGeneratedAdaptorTypes().put("base", baseGen.getGeneratedName());
//        PgEntityAdaptorGenerator pgGen = new PgEntityAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        getEntityDacGenContext().getGeneratedAdaptorTypes().put(GspDbType.PgSQL.toString(), pgGen.getGeneratedName());
//        MySqlAdaptorGenerator mysqlGen = new MySqlAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        getEntityDacGenContext().getGeneratedAdaptorTypes().put(GspDbType.MySQL.toString(), mysqlGen.getGeneratedName());
//        SqlEntityAdaptorGenerator sqlServerGen = new SqlEntityAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        ((EntityDacGenContext) getContext()).getGeneratedAdaptorTypes().put(GspDbType.SQLServer.toString(), sqlServerGen.getGeneratedName());
//        OraEntityAdaptorGenerator oracleGen = new OraEntityAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        entityDacGenContext.getGeneratedAdaptorTypes().put(GspDbType.Oracle.toString(), oracleGen.getGeneratedName());
//        DmEntityAdaptorGenerator dmGen = new DmEntityAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        entityDacGenContext.getGeneratedAdaptorTypes().put(GspDbType.DM.toString(), dmGen.getGeneratedName());
//        HighGoEntityAdaptorGenerator highgoGen = new HighGoEntityAdaptorGenerator(entityDacGenContext, NodeCode, getIsChildDac(), BaseAssembler, ExtendAssemblers);
//        getEntityDacGenContext().getGeneratedAdaptorTypes().put(GspDbType.HighGo.toString(), highgoGen.getGeneratedName());

//        extendGens.add(entityItemGen);
        extendGens.add(baseGen);
        return extendGens;
    }

    private void addChildDacGenerator(java.util.ArrayList<BaseGenerator> childDacGens) {
        if(gspCommonObject.getContainChildObjects() != null && gspCommonObject.getContainChildObjects().size() > 0){
            for (IGspCommonObject commonObject:gspCommonObject.getContainChildObjects()){
                String nodeCode = commonObject.getCode();
                ChildEntityDacGenerator childAssembler = new ChildEntityDacGenerator(commonObject, ReposContext, nodeCode);
                //Create(RepositoryGenContext
                getEntityDacGenContext().getChildDacTypes().put(childAssembler.NodeCode, childAssembler.getName());
                childDacGens.add(childAssembler);
            }
        }
//        for (Map.Entry<String, AssemblerInfo> childAssemblerItem : ChildAssemblerTypes.entrySet()) {
//
//            String nodeCode = childAssemblerItem.getKey();
//
//            AssemblerInfo childAssemblerType = childAssemblerItem.getValue();
//            java.util.ArrayList<AssemblerInfo> childExtendTypes = null;
//            if (ChildExtendAssemblers != null && ChildExtendAssemblers.size() > 0 && ChildExtendAssemblers.containsKey(nodeCode)) {
//                childExtendTypes = ChildExtendAssemblers.get(nodeCode);
//            }
//            ChildEntityDacGenerator childAssembler = new ChildEntityDacGenerator(ReposContext, nodeCode);
//            //Create(RepositoryGenContext
//            getEntityDacGenContext().getChildDacTypes().put(childAssembler.NodeCode, childAssembler.getName());
//            childDacGens.add(childAssembler);
//        }
    }

    @Override
    protected GetAdaptorGenerator getAdaptorMethodGenerator() {
        return new GetEntityAdaptorGenerator(AdaptorFieldName, getEntityDacGenContext(), this.NodeCode);
    }
}
